package com.kx.kxblog.aop;
import com.kx.kxblog.annotation.AutoLog;
import com.kx.kxblog.annotation.DefaultResult;
import com.kx.kxblog.annotation.ResultController;
import com.kx.kxblog.bean.Result;
import com.kx.kxblog.enums.ResultCode;
import com.kx.kxblog.exception.ResultException;
import com.kx.kxblog.util.StringUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestControllerAdvice
public class ResultHandlerAdvice implements ResponseBodyAdvice<Object> {
    private final List<Class<? extends Exception>> clsList = Arrays.asList(IllegalArgumentException.class, HttpRequestMethodNotSupportedException.class, ClientAbortException.class);

    public ResultHandlerAdvice() {
    }

    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Method method = returnType.getMethod();
        if(!returnType.getContainingClass().isAnnotationPresent(ResultController.class)) return body;
        AutoLog autoLog = method.getAnnotation(AutoLog.class);
        HttpServletRequest req = ((ServletServerHttpRequest)request).getServletRequest();
        if (autoLog != null) {
//            if (body == null) {
//                LogToolkit.info(autoLog.value(), req);
//            } else {
//                LogToolkit.info(autoLog.value(), body, req);
//            }
        }

        if (body instanceof Result) {
            return body;
        } else if (body instanceof Resource) {
            return body;
        } else {
            DefaultResult defaultResult = method.getAnnotation(DefaultResult.class);
            String msg;
            Result<Object> result = ResultCode.SUCCESS.getResult();
            if (defaultResult != null) {
                msg = defaultResult.value();
                if(defaultResult.code()!=ResultCode.SUCCESS) {
                    if (StringUtils.isEmpty(msg)) {
                        msg = defaultResult.code().getMsg();
                    }
                }
                result = new Result<>(defaultResult.code().getCode(), msg);
            } else {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                if (apiOperation != null && StringUtils.isNotEmpty(apiOperation.value())) {
                    result.setMsg(apiOperation.value() + "成功");
                }
            }
            result.setData(body);
            return result;
        }
    }

    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception ex, HttpServletRequest request, HttpServletResponse response) {
        if (ex instanceof UndeclaredThrowableException) {
            ex = (Exception)((UndeclaredThrowableException)ex).getUndeclaredThrowable();
        }
        Result<Object> result;
        if (ex instanceof ResultException) {
            ResultException exception = (ResultException)ex;
            result = exception.getResult();
        } else if(ex instanceof MethodArgumentNotValidException){ // 参数校验异常
            MethodArgumentNotValidException validException = (MethodArgumentNotValidException) ex;
            List<FieldError> allErrors = validException.getBindingResult().getFieldErrors();
            List<String> errorList = allErrors.stream().map(error -> error.getObjectName()+"."+error.getField() + error.getDefaultMessage()).collect(Collectors.toList());
            String msg = String.join("\n", errorList);
            return new Result<>(ResultCode.PARAMETER_ERROR.getCode(), msg, allErrors);
        } else {
            String message = ex.getMessage();
            if(StringUtils.isEmpty(message))
                message = ex.toString();
            result = new Result<>(ResultCode.FAIELD.getCode(), message);
            if (!this.clsList.contains(ex.getClass())) {
                log.error("发生未知异常：", ex);
            }
        }
        return result;
    }
}
